"""
环境搭建说明：
 -此脚本只适用于DSDA版本，可能需要在测试前开启ADB ROOT
 -test_sdk_ms_api放到data下
 -需要设置项：设置模块的DEBUG口,AT口,
 -需要多路拨号，只要增加配置文件中常量的字典的条数即可
-以及目标VPN检查列表
"""

import serial
import time
import os
import sys
import random
import re
import subprocess
import threading

# 定义常量
DEBUG_PORT = "COM4"
AT_PORT = "COM68"
BAUDRATE = 115200
# 定义变量
at_serial = None
debug_serial = None
# 记录运行次数
runTimes = 0
# log文件
DEBUG_LOG = ""
ADB_LOG = ""
AT_LOG = ""


# 创建日志文件
def createLogFile():
    global DEBUG_LOG, ADB_LOG, AT_LOG
    timestamp = time.strftime("%Y-%m-%d_%H_%M_%S", time.localtime())
    DEBUG_LOG = f"DEBUG_LOG_{timestamp}.log"
    ADB_LOG = f"ADB_LOG_{timestamp}.log"
    AT_LOG = f"AT_LOG_{timestamp}.log"

    with open(DEBUG_LOG, "w", encoding="utf-8") as file:
        file.write("DEBUG 日志开始记录...\n")

    with open(ADB_LOG, "w", encoding="utf-8") as file:
        file.write("ADB 日志开始记录...\n")

    with open(AT_LOG, "w", encoding="utf-8") as file:
        file.write("AT 日志开始记录...\n")


# 写日志
def write_log(log, logFile):
    with open(logFile, "a", encoding="utf-8") as file_object:
        file_object.write(
            str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
            + "\r\n"
            + log
            + "\r\n"
        )
        now_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print("[" + now_time + "]" + " " + log)


# Send at instruction
def at_write(command, timeout, at_port_opened, at_result, RDY=0, CPIN=0):
    at_command = command.encode("UTF-8")
    at_port_opened.write(at_command)


# 读取at口返回
def at_read(serial_port, logFile=""):
    rec_str = ""
    while True:
        time.sleep(1)
        lenth = serial_port.inWaiting()
        if lenth > 0:
            rec_str = serial_port.read(size=lenth)
            try:
                rec_str = re.sub("\\x1b.*?m", "", rec_str.decode("UTF-8"), 0, re.I)
                if logFile != "":
                    with open(logFile, "a") as file_object:
                        rec_str = (
                            str(time.time())
                            + ":"
                            + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                            + "\r\n"
                            + rec_str
                        )
                        file_object.write(
                            str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                            + "\r\n"
                            + rec_str
                            + "\r\n"
                        )
                return rec_str
            except:
                return ""
        else:
            break
    return rec_str


# 登录设备
def loginDevice():
    global DEBUG_LOG
    # 确认登录失败的条件，或者，不可能失败
    while True:
        #   print("等待登录... ... ")
        write_log("等待登录... ... ", DEBUG_LOG)
        time.sleep(3)
        at_write("\r\n", 3, debug_serial, "OK")
        result = at_read(debug_serial)

        with open(DEBUG_LOG, "a") as file_object:
            file_object.write(result)

        if "login:" in result:
            at_write("root\r\n", 3, debug_serial, "OK")
            #    print("账号输入成功")
            write_log("账号输入成功", DEBUG_LOG)
            time.sleep(2)
            at_write("oelinux123\r\n", 3, debug_serial, "OK")
            #    print("密码输入成功")
            write_log("密码输入成功", DEBUG_LOG)
        elif "~ #" in result:
            #    print("登录成功")
            write_log("登录成功", DEBUG_LOG)
            break
        else:
            continue


# 检查debug口通信状态
def check_port_status():
    global debug_serial
    if debug_serial == None:
        try:
            debug_serial = serial.Serial(DEBUG_PORT, BAUDRATE)
        except:
            print("端口连接失败，请检查端口")
            sys.exit()


# 写入运行次数到日志
def writeRuntimesToLog():
    global runTimes
    with open(DEBUG_LOG, "a+") as file_object:
        file_object.write(
            "\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20)
        )
    #  print("\r\n{} runtimes: {} {}\r\n".format("<"*20,runTimes,">"*20))
    with open(AT_LOG, "a+") as file_object:
        file_object.write(
            "\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20)
        )
    #  print("\r\n{} runtimes: {} {}\r\n".format("<"*20,runTimes,">"*20))
    with open(ADB_LOG, "a+") as file_object:
        file_object.write(
            "\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20)
        )
    print("\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20))
    runTimes += 1


# 发送AT指令
def send_at_command(command):
    # 打开串口
    ser = serial.Serial(AT_PORT, BAUDRATE)
    ser.write((command + "\r\n").encode())
    time.sleep(2)
    response = ser.read_all().decode()
    ser.close()
    with open(AT_LOG, "a", encoding="utf-8") as file_object:
        file_object.write(
            str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
            + "\r\n"
            + response
            + "\r\n"
        )
    return response


# 发送adb命令
def send_adb_command(command):
    try:
        # 开启adb root
        subprocess.run(["adb", "root"], capture_output=True, text=True)
        # 使用 subprocess 模块执行 adb shell 命令
        result = subprocess.run(
            ["adb", "shell", command], capture_output=True, text=True
        )
        with open(ADB_LOG, "a", encoding="utf-8") as file_object:
            file_object.write(
                str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                + "\r\n"
                + result.stdout
                + "\r\n"
            )
        # 返回命令的输出结果
        return result.stdout
    except Exception as e:
        return str(e)


# 查看APN
def check_APN(sim_id):
    write_log(f"开始查看卡槽{sim_id}的APN列表", AT_LOG)
    result = send_at_command("at$qcsimapp={};+cgdcont?".format(sim_id))
    return result


# 删除APN
def remove_APN(sim_id, apn_id):
    write_log(f"开始删除卡槽{sim_id}的APN{apn_id}", AT_LOG)
    send_at_command("at$qcsimapp={};+cgdcont={}".format(sim_id, apn_id))


# 写入运行次数到日志
def writeRuntimesToLog():
    global runTimes
    with open(DEBUG_LOG, "a+") as file_object:
        file_object.write(
            "\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20)
        )
    print("\r\n{} runtimes: {} {}\r\n".format("<" * 20, runTimes, ">" * 20))
    runTimes += 1


# 进入测试接口
def intoTestInterface_testsdk(debug_serial, logFile):
    at_write("cd /data\r\n./test_sdk_ms_api\r\n", 3, debug_serial, "OK")
    write_log("进入测试接口", logFile)
    time.sleep(1)
    at_write("5\n", 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("0\n", 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    result = at_read(debug_serial, logFile)


# 退出测试接口
def exit_testsdk(debug_serial, logFile):
    at_write("-1\n-1\n", 3, debug_serial, "OK")
    result = at_read(debug_serial, logFile)


# 返回测试接口
def back_testsdk(debug_serial, logFile):
    at_write("-1\n", 3, debug_serial, "OK")
    result = at_read(debug_serial, logFile)


# 创建测试APN
def set_apn_testsdk(testapn, testapn_name, simid):
    write_log(f"卡{simid}开始创建测试APN{testapn}，名称{testapn_name}", DEBUG_LOG)
    time.sleep(1)
    at_write("10\n", 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("{}\n".format(testapn), 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("3\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("3\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("{}\n".format(testapn_name), 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_write("{}\n".format(simid), 3, debug_serial, "OK")
    time.sleep(1)
    result = at_read(debug_serial, DEBUG_LOG)
    if "Successful" in result:
        write_log(f"测试APN{testapn}创建成功", DEBUG_LOG)
    else:
        write_log(f"测试APN{testapn}创建失败", DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)


# 创建并设置datacall
def set_datacall1_testsdk(datacall_id, datacall_name, testapn, simid):
    write_log(
        f"卡{simid}开始设置datacall_{datacall_id}，名称{datacall_name}设置的APN为{testapn}路",
        DEBUG_LOG,
    )
    time.sleep(1)
    at_write("1\n", 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("{}\n".format(datacall_name), 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("1\n", 3, debug_serial, "OK")
    time.sleep(2)
    result1 = at_read(debug_serial, DEBUG_LOG)
    if "Successful" in result1:
        write_log(f"datacall{datacall_id}创建成功", DEBUG_LOG)
    else:
        write_log(f"datacall{datacall_id}创建失败", DEBUG_LOG)

    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("2\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("{}\n".format(testapn), 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("1\n", 3, debug_serial, "OK")
    at_read(debug_serial, DEBUG_LOG)
    time.sleep(1)
    at_write("0\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_write("{}\n".format(simid), 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("y\n", 3, debug_serial, "OK")
    time.sleep(1)
    result2 = at_read(debug_serial, DEBUG_LOG)
    if "Successful" in result2:
        write_log(f"datacall{datacall_id}设置APN{testapn}成功", DEBUG_LOG)
    else:
        write_log(f"datacall{datacall_id}设置APN{testapn}失败", DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(3)
    at_read(debug_serial, DEBUG_LOG)


# 启动datacall
def start_datacall_testsdk(datacall_id):
    write_log(f"开始拨号datacall：{datacall_id}", DEBUG_LOG)
    at_write("4\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    time.sleep(1)
    result = at_read(debug_serial, DEBUG_LOG)
    if "Successful" in result:
        write_log(f"datacall{datacall_id}拨号成功", DEBUG_LOG)
    else:
        write_log(f"datacall{datacall_id}拨号失败", DEBUG_LOG)
    at_write("8\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)


# 删除datacall
def remove_datacall_testsdk(datacall_id):
    time.sleep(1)
    write_log(f"开始停止并删除datacall：{datacall_id}", DEBUG_LOG)
    at_write("5\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("6\n", 3, debug_serial, "OK")
    time.sleep(1)
    at_read(debug_serial, DEBUG_LOG)
    at_write("{}\n".format(datacall_id), 3, debug_serial, "OK")
    time.sleep(1)
    result = at_read(debug_serial, DEBUG_LOG)
    if "Successful" in result:
        write_log(f"datacall{datacall_id}删除成功", DEBUG_LOG)
    else:
        write_log(f"datacall{datacall_id}删除失败", DEBUG_LOG)
    at_write("\n", 3, debug_serial, "OK")
    time.sleep(1)


# 开始查看网卡信息
def check_network_info():
    write_log("开始查看网卡信息", AT_LOG)
    result = send_adb_command("ifconfig")
    return result


# 通过ADB命令ping测试
def ping_testsdk(test_url, rmnet_info):
    write_log("通过ADB调试开始ping网测试", ADB_LOG)
    for interface in rmnet_info.keys():
        # result = send_adb_command(f"ping -I {interface} -c 10 {test_url}")
        result = send_adb_command(f"ping -I {interface} -c 4 {test_url}")
        if " 100%" in result:
            write_log(f"{interface},丢包率为100%", ADB_LOG)
        else:
            write_log(f"{interface},丢包率不为100%,测试通过", ADB_LOG)


# 配置路由
def route_config(rmnet_info):
    write_log("开始配置路由", ADB_LOG)
    for interface, info in rmnet_info.items():
        send_adb_command(
            f"iptables -t nat -A POSTROUTING -o {interface} -j MASQUERADE --random"
        )
        result = send_adb_command(
            f"ip route add default via {info['ip']} dev {interface} "
        )


def extract_rmnet_info(ifconfig_output):
    # 定义匹配 rmnet 网卡的正则表达式
    pattern = re.compile(
        r"(rmnet\w+):.*?inet (\d+\.\d+\.\d+\.\d+).*?netmask (\d+\.\d+\.\d+\.\d+)", re.S
    )
    matches = pattern.findall(ifconfig_output)
    # 构造结果字典
    rmnet_info = {}
    for match in matches:
        interface, ip, netmask = match
        rmnet_info[interface] = {"ip": ip, "netmask": netmask}
    return rmnet_info


def extract_apn_ids(at_response):
    apn_ids = []
    # 按行拆分 AT 响应
    lines = at_response.splitlines()

    for line in lines:
        # 检查行是否以 "+CGDCONT" 开头
        if line.startswith("+CGDCONT"):
            # 按逗号分割并提取第一个字段（包含 ID）
            parts = line.split(",")
            if len(parts) > 0:
                # 提取出 "+CGDCONT: ID" 中的 ID 部分
                apn_id = parts[0].split(":")[1].strip()
                apn_ids.append(apn_id)

    write_log(f"APN ID 列表：{apn_ids}", AT_LOG)
    return apn_ids


def check_apn_in_list(apn_ids, target_ids):
    # 遍历每个目标ID，检查是否在apn_ids列表中
    for target_id in target_ids:
        if target_id in apn_ids:
            # print(f"APN ID {target_id} 存在于结果中")
            write_log(f"APN ID {target_id} 存在于结果中", AT_LOG)
        else:
            # print(f"APN ID {target_id} 不存在于结果中")
            write_log(f"APN ID {target_id} 不存在于结果中", AT_LOG)


# 常量定义
# 测试目标地址（网络连通性测试用）
# 使用 Google 公共 DNS 地址 8.8.8.8，可靠性高
TEST_URL = "8.8.8.8"

# APN 配置信息列表
# 每个字典表示一个 APN（接入点名称）的配置项，字段说明：
# - id: APN 的唯一标识符，用于区分不同 APN。
# - name: APN 的名称，用于标识测试的 APN。
# - sim_slot: SIM 卡号，用于区分多张卡。
# - sim_id: 使用的 SIM 卡槽位（如卡槽 0 或卡槽 1）。
APN_CONFIGS = [
    {"id": "9", "name": "test1", "sim_slot": "1", "sim_id": "0"},
    {"id": "11", "name": "test2", "sim_slot": "1", "sim_id": "0"},
    # {"id": "9", "name": "test1", "sim_slot": "2", "sim_id": "1"},
    # {"id": "11", "name": "test2", "sim_slot": "2", "sim_id": "1"},
]

# 数据呼叫（Datacall）配置信息列表
# 每个字典表示一个 datacall 配置项，字段说明：
# - id: datacall 的唯一标识符，用于区分不同数据呼叫。
# - name: datacall 的名称，用于标识测试的 datacall。
# - apn_id: 关联的 APN 的 ID，表明该数据呼叫使用哪个接入点。
# - sim_slot: 使用的 SIM 卡号。
DATACALL_CONFIGS = [
    {"id": "1", "name": "call1", "apn_id": "9", "sim_slot": "1"},
    {"id": "2", "name": "call2", "apn_id": "11", "sim_slot": "1"},
    # {"id": "3", "name": "call3", "apn_id": "9", "sim_slot": "2"},
    # {"id": "4", "name": "call4", "apn_id": "11", "sim_slot": "2"},
]

# 目标 APN ID 列表，也可以自行添加或修改
# 用于验证已配置的 APN 是否与测试目标匹配
# 包含两个目标 ID："9" 和 "11"。
TARGET_IDS = ["9", "11"]


# 功能函数
def initialize_logs():
    createLogFile()
    print(f"DEBUG_LOG: {DEBUG_LOG}, ADB_LOG: {ADB_LOG}, AT_LOG: {AT_LOG}")


def check_and_login():
    check_port_status()
    loginDevice()
    intoTestInterface_testsdk(debug_serial, DEBUG_LOG)


def setup_apns_and_datacalls():
    for apn in APN_CONFIGS:
        set_apn_testsdk(apn["id"], apn["name"], apn["sim_slot"])
    for datacall in DATACALL_CONFIGS:
        set_datacall1_testsdk(
            datacall["id"], datacall["name"], datacall["apn_id"], datacall["sim_slot"]
        )
        start_datacall_testsdk(datacall["id"])


def cleanup_resources():
    for datacall in DATACALL_CONFIGS:
        remove_datacall_testsdk(datacall["id"])
    for apn in APN_CONFIGS:
        remove_APN(apn["sim_id"], apn["id"])


def verify_apns():
    for sim_id in ["0", "1"]:
        apn_response = check_APN(sim_id)
        apn_ids = extract_apn_ids(apn_response)
        check_apn_in_list(apn_ids, TARGET_IDS)


def perform_ping_test():
    ifconfig_output = check_network_info()
    rmnet_info = extract_rmnet_info(ifconfig_output)
    print(f"Extracted rmnet info: {rmnet_info}")
    route_config(rmnet_info)
    ping_testsdk(TEST_URL, rmnet_info)


# 主函数
def main():
    try:
        initialize_logs()
        check_and_login()

        while True:
            writeRuntimesToLog()
            verify_apns()
            setup_apns_and_datacalls()
            perform_ping_test()
            cleanup_resources()
    except Exception as e:
        print(f"Error occurred: {e}")
    finally:
        print("Test execution finished.")


if __name__ == "__main__":
    main()
